/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/

#include "fancytabs.h"
#include "stylehelper.h"

#include <QDebug>

#include <QAnimationGroup>
#include <QColorDialog>
#include <QHBoxLayout>
#include <QMenu>
#include <QMouseEvent>
#include <QPainter>
#include <QPropertyAnimation>
#include <QSignalMapper>
#include <QSplitter>
#include <QStackedLayout>
#include <QStyleOptionTabV3>
#include <QToolButton>
#include <QToolTip>
#include <QVBoxLayout>
//#include <QWindowsStyle>

using namespace Core;
using namespace Internal;

const int FancyTabBar::m_rounding = 22;
const int FancyTabBar::m_textPadding = 4;

void FancyTabProxyStyle::drawControl(
        ControlElement element, const QStyleOption* option,
        QPainter* p, const QWidget* widget) const {

    const QStyleOptionTabV3* v_opt = qstyleoption_cast<const QStyleOptionTabV3*>(option);

    if (element != CE_TabBarTab || !v_opt) {
        QProxyStyle::drawControl(element, option, p, widget);
        return;
    }

    const QRect rect = v_opt->rect;
    const bool selected = v_opt->state & State_Selected;
    const bool vertical_tabs = v_opt->shape == QTabBar::RoundedWest;
    const QString text = v_opt->text;

    if (selected) {
        //background
        p->save();
        QLinearGradient grad(rect.topLeft(), rect.topRight());
        grad.setColorAt(0, QColor(255, 255, 255, 140));
        grad.setColorAt(1, QColor(255, 255, 255, 210));
        p->fillRect(rect.adjusted(0, 0, 0, -1), grad);
        p->restore();

        //shadows
        p->setPen(QColor(0, 0, 0, 110));
        p->drawLine(rect.topLeft() + QPoint(1,-1), rect.topRight() - QPoint(0,1));
        p->drawLine(rect.bottomLeft(), rect.bottomRight());
        p->setPen(QColor(0, 0, 0, 40));
        p->drawLine(rect.topLeft(), rect.bottomLeft());

        //highlights
        p->setPen(QColor(255, 255, 255, 50));
        p->drawLine(rect.topLeft() + QPoint(0, -2), rect.topRight() - QPoint(0,2));
        p->drawLine(rect.bottomLeft() + QPoint(0, 1), rect.bottomRight() + QPoint(0,1));
        p->setPen(QColor(255, 255, 255, 40));
        p->drawLine(rect.topLeft() + QPoint(0, 0), rect.topRight());
        p->drawLine(rect.topRight() + QPoint(0, 1), rect.bottomRight() - QPoint(0, 1));
        p->drawLine(rect.bottomLeft() + QPoint(0,-1), rect.bottomRight()-QPoint(0,1));
    }

    QTransform m;
    if (vertical_tabs) {
        m = QTransform::fromTranslate(rect.left(), rect.bottom());
        m.rotate(-90);
    } else {
        m = QTransform::fromTranslate(rect.left(), rect.top());
    }

    const QRect draw_rect(QPoint(0, 0), m.mapRect(rect).size());

    p->save();
    p->setTransform(m);

    QRect icon_rect(QPoint(8, 0), v_opt->iconSize);
    QRect text_rect(icon_rect.topRight() + QPoint(4, 0), draw_rect.size());
    text_rect.setRight(draw_rect.width());
    icon_rect.translate(0, (draw_rect.height() - icon_rect.height()) / 2);

    QFont boldFont(p->font());
    boldFont.setPointSizeF(Utils::StyleHelper::sidebarFontSize());
    boldFont.setBold(true);
    p->setFont(boldFont);
    p->setPen(selected ? QColor(255, 255, 255, 160) : QColor(0, 0, 0, 110));
    int textFlags = Qt::AlignHCenter | Qt::AlignVCenter;
    p->drawText(text_rect, textFlags, text);
    p->setPen(selected ? QColor(60, 60, 60) : Utils::StyleHelper::panelTextColor());
#ifndef Q_WS_MAC
    if (widget) {
        const QString fader_key = "tab_" + text + "_fader";
        const QString animation_key = "tab_" + text + "_animation";

        const QString tab_hover = widget->property("tab_hover").toString();
        int fader = widget->property(fader_key.toUtf8().constData()).toInt();
        QPropertyAnimation* animation = widget->property(animation_key.toUtf8().constData()).value<QPropertyAnimation*>();

        if (!animation) {
            QWidget* mut_widget = const_cast<QWidget*>(widget);
            fader = 0;
            mut_widget->setProperty(fader_key.toUtf8().constData(), fader);
            animation = new QPropertyAnimation(mut_widget, fader_key.toUtf8(), mut_widget);
            connect(animation, SIGNAL(valueChanged(QVariant)), mut_widget, SLOT(update()));
            mut_widget->setProperty(animation_key.toUtf8().constData(), QVariant::fromValue(animation));
        }

        if (text == tab_hover) {
            if (animation->state() != QAbstractAnimation::Running && fader != 40) {
                animation->stop();
                animation->setDuration(80);
                animation->setEndValue(40);
                animation->start();
            }
        } else {
            if (animation->state() != QAbstractAnimation::Running && fader != 0) {
                animation->stop();
                animation->setDuration(160);
                animation->setEndValue(0);
                animation->start();
            }
        }

        if (!selected) {
            p->save();
            QLinearGradient grad(draw_rect.topLeft(), vertical_tabs ? draw_rect.bottomLeft() : draw_rect.topRight());
            grad.setColorAt(0, Qt::transparent);
            grad.setColorAt(0.5, QColor(255, 255, 255, fader));
            grad.setColorAt(1, Qt::transparent);
            p->fillRect(draw_rect, grad);
            p->setPen(QPen(grad, 1.0));
            p->drawLine(draw_rect.topLeft(), vertical_tabs ? draw_rect.bottomLeft() : draw_rect.topRight());
            p->drawLine(draw_rect.bottomRight(), vertical_tabs ? draw_rect.topRight() : draw_rect.bottomLeft());
            p->restore();
        }
    }
#endif

    Utils::StyleHelper::drawIconWithShadow(v_opt->icon, icon_rect, p, QIcon::Normal);

    p->drawText(text_rect.translated(0, -1), textFlags, text);

    p->restore();
}

void FancyTabProxyStyle::polish(QWidget* widget) {
    if (QString(widget->metaObject()->className()) == "QTabBar") {
        widget->setMouseTracking(true);
        widget->installEventFilter(this);
    }
    QProxyStyle::polish(widget);
}

void FancyTabProxyStyle::polish(QApplication* app) {
    QProxyStyle::polish(app);
}

void FancyTabProxyStyle::polish(QPalette& palette) {
    QProxyStyle::polish(palette);
}

bool FancyTabProxyStyle::eventFilter(QObject* o, QEvent* e) {
    QTabBar* bar = qobject_cast<QTabBar*>(o);
    if (bar && (e->type() == QEvent::MouseMove || e->type() == QEvent::Leave)) {
        QMouseEvent* event = static_cast<QMouseEvent*>(e);
        const QString old_hovered_tab = bar->property("tab_hover").toString();
        const QString hovered_tab = e->type() == QEvent::Leave ? QString() : bar->tabText(bar->tabAt(event->pos()));
        bar->setProperty("tab_hover", hovered_tab);

        if (old_hovered_tab != hovered_tab)
            bar->update();
    }

    return false;
}


void TabBar::mouseDoubleClickEvent(QMouseEvent *e)
{
    e->accept();
    int index = tabAt(e->pos());
    if (index != -1) {
        emit tabDoubleClicked(index);
    }
}


FancyTab::FancyTab(QWidget* tabbar)
    : QWidget(tabbar), tabbar(tabbar), m_fader(0)
{
    animator.setPropertyName("fader");
    animator.setTargetObject(this);
    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
}

void FancyTab::fadeIn()
{
    animator.stop();
    animator.setDuration(80);
    animator.setEndValue(40);
    animator.start();
}

void FancyTab::fadeOut()
{
    animator.stop();
    animator.setDuration(160);
    animator.setEndValue(0);
    animator.start();
}

void FancyTab::setFader(float value)
{
    m_fader = value;
    tabbar->update();
}

FancyTabBar::FancyTabBar(QWidget *parent)
    : QWidget(parent)
{
    setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
    //setStyle(new QWindowsStyle);
    setMinimumWidth(qMax(2 * m_rounding, 40));
    setAttribute(Qt::WA_Hover, true);
    setFocusPolicy(Qt::NoFocus);
    setMouseTracking(true); // Needed for hover events
    m_triggerTimer.setSingleShot(true);

    QVBoxLayout* layout = new QVBoxLayout;
    layout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding));
    layout->setSpacing(0);
    layout->setContentsMargins(0, 0, 0, 0);
    setLayout(layout);

    // We use a zerotimer to keep the sidebar responsive
    connect(&m_triggerTimer, SIGNAL(timeout()), this, SLOT(emitCurrentIndex()));
}

FancyTabBar::~FancyTabBar()
{
    //delete style();
}

QSize FancyTab::sizeHint() const {
    QFont boldFont(font());
    boldFont.setPointSizeF(Utils::StyleHelper::sidebarFontSize());
    boldFont.setBold(true);
    QFontMetrics fm(boldFont);
    int spacing = 8;
    int width = 60 + spacing + 2;
    int iconHeight = 32;
    QSize ret(width, iconHeight + spacing + fm.height());
    return ret;
}

QSize FancyTabBar::tabSizeHint(bool minimum) const
{
    QFont boldFont(font());
    boldFont.setPointSizeF(Utils::StyleHelper::sidebarFontSize());
    boldFont.setBold(true);
    QFontMetrics fm(boldFont);
    int spacing = 8;
    int width = 60 + spacing + 2;
    int iconHeight = minimum ? 0 : 32;
    return QSize(width, iconHeight + spacing + fm.height());
}

void FancyTabBar::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event)
    QPainter p(this);

    for (int i = 0; i < count(); ++i)
        if (i != currentIndex())
            paintTab(&p, i);

    // paint active tab last, since it overlaps the neighbors
    if (currentIndex() != -1)
        paintTab(&p, currentIndex());
}

void FancyTab::enterEvent(QEvent*)
{
    fadeIn();
}

void FancyTab::leaveEvent(QEvent*)
{
    fadeOut();
}

void FancyTab::mouseDoubleClickEvent ( QMouseEvent * event )
{
    emit doubleClicked();
    QWidget::mouseDoubleClickEvent(event);
}

QSize FancyTabBar::sizeHint() const
{
    QSize sh = tabSizeHint();
    return QSize(sh.width(), sh.height() * m_tabs.count());
}

QSize FancyTabBar::minimumSizeHint() const
{
    QSize sh = tabSizeHint(true);
    return QSize(sh.width(), sh.height() * m_tabs.count());
}

QRect FancyTabBar::tabRect(int index) const
{
    return m_tabs[index]->geometry();
}

QString FancyTabBar::tabToolTip(int index) const {
    return m_tabs[index]->toolTip();
}

void FancyTabBar::setTabText(const QString & text, int index)
{
    m_tabs[index]->text = text;
    m_tabs[index]->update();
}

void FancyTabBar::setTabToolTip(int index, const QString& toolTip) {
    m_tabs[index]->setToolTip(toolTip);
}

// This keeps the sidebar responsive since
// we get a repaint before loading the
// mode itself
void FancyTabBar::emitCurrentIndex()
{
    emit currentChanged(m_currentIndex);
}

void FancyTabBar::mousePressEvent(QMouseEvent *e)
{
    e->accept();
    for (int index = 0; index < m_tabs.count(); ++index) {
        if (tabRect(index).contains(e->pos())) {
            m_currentIndex = index;
            update();
            m_triggerTimer.start(0);
            break;
        }
    }
}

void FancyTabBar::mouseDoubleClickEvent ( QMouseEvent * e )
{
    e->accept();
    for (int index = 0; index < m_tabs.count(); ++index) {
        if (tabRect(index).contains(e->pos())) {
            emit tabDoubleClicked(index);
            break;
        }
    }
}

void FancyTabBar::addTab(const QIcon& icon, const QString& label) {
    FancyTab *tab = new FancyTab(this);
    tab->icon = icon;
    tab->text = label;
    m_tabs.append(tab);
    qobject_cast<QVBoxLayout*>(layout())->insertWidget(layout()->count()-1, tab);
}

void FancyTabBar::addSpacer(int size, QSizePolicy::Policy vPolicy)
{
    qobject_cast<QVBoxLayout*>(layout())->insertSpacerItem(layout()->count()-1,
                                                           new QSpacerItem(0, size, vPolicy, QSizePolicy::Maximum));
}

void FancyTabBar::removeAllTabs()
{
    foreach ( FancyTab *tab, m_tabs)
        delete tab;
    m_tabs.clear();
}

void FancyTabBar::paintTab(QPainter *painter, int tabIndex) const
{
    if (!validIndex(tabIndex)) {
        qWarning("invalid index");
        return;
    }
    painter->save();

    QRect rect = tabRect(tabIndex);
    bool selected = (tabIndex == m_currentIndex);

    if (selected) {
        //background
        painter->save();
        QLinearGradient grad(rect.topLeft(), rect.topRight());
        grad.setColorAt(0, QColor(255, 255, 255, 140));
        grad.setColorAt(1, QColor(255, 255, 255, 210));
        painter->fillRect(rect.adjusted(0, 0, 0, -1), grad);
        painter->restore();

        //shadows
        painter->setPen(QColor(0, 0, 0, 110));
        painter->drawLine(rect.topLeft() + QPoint(1,-1), rect.topRight() - QPoint(0,1));
        painter->drawLine(rect.bottomLeft(), rect.bottomRight());
        painter->setPen(QColor(0, 0, 0, 40));
        painter->drawLine(rect.topLeft(), rect.bottomLeft());

        //highlights
        painter->setPen(QColor(255, 255, 255, 50));
        painter->drawLine(rect.topLeft() + QPoint(0, -2), rect.topRight() - QPoint(0,2));
        painter->drawLine(rect.bottomLeft() + QPoint(0, 1), rect.bottomRight() + QPoint(0,1));
        painter->setPen(QColor(255, 255, 255, 40));
        painter->drawLine(rect.topLeft() + QPoint(0, 0), rect.topRight());
        painter->drawLine(rect.topRight() + QPoint(0, 1), rect.bottomRight() - QPoint(0, 1));
        painter->drawLine(rect.bottomLeft() + QPoint(0,-1), rect.bottomRight()-QPoint(0,1));
    }

    QString tabText(painter->fontMetrics().elidedText(this->tabText(tabIndex), Qt::ElideMiddle, width()));
    QRect tabTextRect(tabRect(tabIndex));
    QRect tabIconRect(tabTextRect);
    tabIconRect.adjust(+4, +4, -4, -4);
    tabTextRect.translate(0, -2);
    QFont boldFont(painter->font());
    boldFont.setPointSizeF(Utils::StyleHelper::sidebarFontSize());
    boldFont.setBold(true);
    painter->setFont(boldFont);
    painter->setPen(selected ? QColor(255, 255, 255, 160) : QColor(0, 0, 0, 110));
    int textFlags = Qt::AlignCenter | Qt::AlignBottom;
    painter->drawText(tabTextRect, textFlags, tabText);
    painter->setPen(selected ? QColor(60, 60, 60) : Utils::StyleHelper::panelTextColor());
#ifndef Q_WS_MAC
    if (!selected) {
        painter->save();
        int fader = int(m_tabs[tabIndex]->fader());
        QLinearGradient grad(rect.topLeft(), rect.topRight());
        grad.setColorAt(0, Qt::transparent);
        grad.setColorAt(0.5, QColor(255, 255, 255, fader));
        grad.setColorAt(1, Qt::transparent);
        painter->fillRect(rect, grad);
        painter->setPen(QPen(grad, 1.0));
        painter->drawLine(rect.topLeft(), rect.topRight());
        painter->drawLine(rect.bottomLeft(), rect.bottomRight());
        painter->restore();
    }
#endif

    const int textHeight = painter->fontMetrics().height();
    tabIconRect.adjust(0, 4, 0, -textHeight);
    Utils::StyleHelper::drawIconWithShadow(tabIcon(tabIndex), tabIconRect, painter, QIcon::Normal);

    painter->translate(0, -1);
    painter->drawText(tabTextRect, textFlags, tabText);
    painter->restore();
}

void FancyTabBar::setCurrentIndex(int index) {
    m_currentIndex = index;
    update();
    emit currentChanged(m_currentIndex);
}

QString FancyTabBar::currentText() const {
    return m_currentIndex >= m_tabs.count() || m_currentIndex < 0
            ? QString() : m_tabs.at(m_currentIndex)->text;
}

//////
// FancyColorButton
//////

class FancyColorButton : public QWidget
{
public:
    FancyColorButton(QWidget *parent)
        : m_parent(parent)
    {
        setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
    }

    void mousePressEvent(QMouseEvent *ev)
    {
        if (ev->modifiers() & Qt::ShiftModifier)
            Utils::StyleHelper::setBaseColor(QColorDialog::getColor(Utils::StyleHelper::requestedBaseColor(), m_parent));
    }
private:
    QWidget *m_parent;
};

//////
// FancyTabWidget
//////

FancyTabWidget::FancyTabWidget(QWidget* parent)
    : QWidget(parent),
      mode_(Mode_None),
      current_index_(-1),
      tab_bar_(NULL),
      stack_(new QStackedLayout),
      side_widget_(new QWidget),
      side_layout_(new QVBoxLayout),
      top_layout_(new QVBoxLayout),
      use_background_(false),
      menu_(NULL),
      proxy_style_(new FancyTabProxyStyle)
{
    side_layout_->setSpacing(0);
    side_layout_->setMargin(0);
    side_layout_->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding));

    side_widget_->setLayout(side_layout_);
    side_widget_->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);

    top_layout_->setMargin(0);
    top_layout_->setSpacing(0);
    top_layout_->addLayout(stack_);

    QHBoxLayout* main_layout = new QHBoxLayout;
    main_layout->setMargin(0);
    main_layout->setSpacing(1);
    main_layout->addWidget(side_widget_);
    main_layout->addLayout(top_layout_);
    setLayout(main_layout);
}

void FancyTabWidget::AddTab(QWidget* tab, const QIcon& icon, const QString& label) {
    stack_->addWidget(tab);
    items_ << Item(icon, label);
    show();
}

void FancyTabWidget::AddSpacer(int size, QSizePolicy::Policy vPolicy) {
    items_ << Item(size, vPolicy);
}


void FancyTabWidget::removeTab(const QString &label)
{
    int index=-1;
    for (int i=0; i< items_.count(); i++)
        if (items_[i].tab_label_ == label) {
            index = i;
            break;
        }

    removeTab(index);
}


void FancyTabWidget::removeTab(int index)
{
    if (index < 0)
        return;

    QWidget * widget = stack_->widget(index);
    stack_->removeWidget(widget);
    items_.removeAt(index);
    if (!items_.size())
        hide();
}


void FancyTabWidget::deleteTab(const QString &label)
{
    int index=-1;
    for (int i=0; i< items_.count(); i++)
        if (items_[i].tab_label_ == label) {
            index = i;
            break;
        }

    deleteTab(index);
}


void FancyTabWidget::deleteTab(int index)
{
    if (index < 0)
        return;

    QWidget * widget = stack_->widget(index);
    stack_->removeWidget(widget);
    delete widget;
    items_.removeAt(index);
    if (!items_.size())
        hide();
}

void FancyTabWidget::removeAllTabs()
{
    for (int i=items_.count()-1; i>=0; --i)
        stack_->removeWidget(stack_->widget(i));
    items_.clear();
    current_index_ = -1;
    hide();
}

void FancyTabWidget::deleteAllTabs()
{
    for (int i=items_.count()-1; i>=0; --i ) {
        QWidget * widget = stack_->widget(i);
        stack_->removeWidget(widget);
        delete widget;
    }
    items_.clear();
    current_index_ = -1;
    hide();
}

int FancyTabWidget::tabsCount()
{
    return items_.count();
}

void FancyTabWidget::SetBackgroundPixmap(const QPixmap& pixmap) {
    background_pixmap_ = pixmap;
    update();
}

void FancyTabWidget::paintEvent(QPaintEvent*) {
    if (!use_background_)
        return;

    QPainter painter(this);

    QRect rect = side_widget_->rect().adjusted(0, 0, 1, 0);
    rect = style()->visualRect(layoutDirection(), geometry(), rect);
    Utils::StyleHelper::verticalGradient(&painter, rect, rect);

    if (!background_pixmap_.isNull()) {
        QRect pixmap_rect(background_pixmap_.rect());
        pixmap_rect.moveTo(rect.topLeft());

        while (pixmap_rect.top() < rect.bottom()) {
            QRect source_rect(pixmap_rect.intersected(rect));
            source_rect.moveTo(0, 0);
            painter.drawPixmap(pixmap_rect.topLeft(), background_pixmap_, source_rect);
            pixmap_rect.moveTop(pixmap_rect.bottom() - 10);
        }
    }

    painter.setPen(Utils::StyleHelper::borderColor());
    painter.drawLine(rect.topRight(), rect.bottomRight());

    QColor light = Utils::StyleHelper::sidebarHighlight();
    painter.setPen(light);
    painter.drawLine(rect.bottomLeft(), rect.bottomRight());
}

int FancyTabWidget::current_index() const {
    return current_index_;
}

QString FancyTabWidget::current_text() const
{
    return items_.isEmpty() || current_index_ >= items_.count() || current_index_ < 0
            ? QString() : items_.at(current_index_).tab_label_;
}


void FancyTabWidget::SetCurrentIndex(int index)
{
    current_index_ = index;
    if (FancyTabBar* bar = qobject_cast<FancyTabBar*>(tab_bar_)) {
        bar->setCurrentIndex(index);
    } else if (QTabBar* bar = qobject_cast<QTabBar*>(tab_bar_)) {
        bar->setCurrentIndex(index);
    } else {
        stack_->setCurrentIndex(index);
    }
}


void FancyTabWidget::changeText(const QString & text, int index)
{
    if (FancyTabBar* bar = qobject_cast<FancyTabBar*>(tab_bar_)) {
        bar->setTabText(text, index);
    } else if (QTabBar* bar = qobject_cast<QTabBar*>(tab_bar_)) {
        bar->setTabText(index, text);
    }
    items_[index].tab_label_ = text;
}


QString FancyTabWidget::tabText(int index)
{
    if (index<0 && index>=items_.count())
        return QString();
    return items_.at(index).tab_label_;
//    if (FancyTabBar* bar = qobject_cast<FancyTabBar*>(tab_bar_)) {
//        return bar->tabText(index);
//    } else if (QTabBar* bar = qobject_cast<QTabBar*>(tab_bar_)) {
//        return bar->tabText(index);
//    }
//    return QString();
}


void FancyTabWidget::ShowWidget(int index) {
    current_index_ = index;
    stack_->setCurrentIndex(index);
    emit CurrentChanged(index);
}

void FancyTabWidget::AddBottomWidget(QWidget* widget) {
    top_layout_->addWidget(widget);
}

void FancyTabWidget::updateState()
{
    SetMode(mode_);
    //    if (current_index_>=0) {
    //        this->blockSignals(true);
    //        SetCurrentIndex(current_index_);
    //        this->blockSignals(false);
    //    }
}

void FancyTabWidget::SetMode(Mode mode) {
    // Remove previous tab bar
    delete tab_bar_;
    tab_bar_ = NULL;

    use_background_ = false;

    // Create new tab bar
    switch (mode) {
    case Mode_None:
    default:
        qWarning() << "Unknown fancy tab mode" << mode;
        // fallthrough

    case Mode_LargeSidebar: {
        FancyTabBar* bar = new FancyTabBar(this);
        side_layout_->insertWidget(0, bar);
        tab_bar_ = bar;

        foreach (const Item& item, items_) {
            if (item.type_ == Item::Type_Spacer)
                bar->addSpacer(item.spacer_size_);
            else
                bar->addTab(item.tab_icon_, item.tab_label_);
        }

        bar->setCurrentIndex(stack_->currentIndex());
        connect(bar, SIGNAL(currentChanged(int)), SLOT(ShowWidget(int)));
        connect(bar, SIGNAL(tabDoubleClicked(int)), this, SIGNAL(tabDoubleClicked(int)));

        use_background_ = true;

        break;
    }

    case Mode_Tabs:
        MakeTabBar(QTabBar::RoundedNorth, true, false, false);
        break;

    case Mode_IconOnlyTabs:
        MakeTabBar(QTabBar::RoundedNorth, false, true, false);
        break;

    case Mode_SmallSidebar:
        MakeTabBar(QTabBar::RoundedWest, true, true, true);
        use_background_ = true;
        break;

    case Mode_PlainSidebar:
        MakeTabBar(QTabBar::RoundedWest, true, true, false);
        break;
    }

    tab_bar_->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);

    mode_ = mode;
    emit ModeChanged(mode);
    update();
}

void FancyTabWidget::contextMenuEvent(QContextMenuEvent* e) {
    if ((side_widget_ && !side_widget_->geometry().contains(e->pos())) &&
            (tab_bar_ && !tab_bar_->geometry().contains(e->pos())))
        return;

    if (!menu_) {
        menu_ = new QMenu(this);

        QSignalMapper* mapper = new QSignalMapper(this);
        QActionGroup* group = new QActionGroup(this);
        AddMenuItem(mapper, group, tr("Large sidebar"), Mode_LargeSidebar);
        AddMenuItem(mapper, group, tr("Small sidebar"), Mode_SmallSidebar);
        AddMenuItem(mapper, group, tr("Plain sidebar"), Mode_PlainSidebar);
        AddMenuItem(mapper, group, tr("Tabs on top"), Mode_Tabs);
        AddMenuItem(mapper, group, tr("Icons on top"), Mode_IconOnlyTabs);
        menu_->addActions(group->actions());

        connect(mapper, SIGNAL(mapped(int)), SLOT(SetMode(int)));
    }

    menu_->popup(e->globalPos());
}

void FancyTabWidget::AddMenuItem(QSignalMapper* mapper, QActionGroup* group,
                                 const QString& text, Mode mode) {
    QAction* action = group->addAction(text);
    action->setCheckable(true);
    mapper->setMapping(action, mode);
    connect(action, SIGNAL(triggered()), mapper, SLOT(map()));

    if (mode == mode_)
        action->setChecked(true);
}
QColor FancyTabWidget::baseColor() const
{
    return m_baseColor;
}

void FancyTabWidget::setBaseColor(const QColor &baseColor)
{
    m_baseColor = baseColor;
}


void FancyTabWidget::MakeTabBar(QTabBar::Shape shape, bool text, bool icons,
                                bool fancy) {
    TabBar* bar = new TabBar(this);
    bar->setShape(shape);
    bar->setDocumentMode(true);
    bar->setUsesScrollButtons(true);
    bar->setElideMode(Qt::ElideRight);
    //bar->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);

    if (shape == QTabBar::RoundedWest) {
        bar->setIconSize(QSize(22, 22));
    }

    if (fancy) {
        bar->setStyle(proxy_style_.data());
    }

    if (shape == QTabBar::RoundedNorth)
        top_layout_->insertWidget(0, bar);
    else
        side_layout_->insertWidget(0, bar);

    foreach (const Item& item, items_) {
        if (item.type_ != Item::Type_Tab)
            continue;

        QString label = item.tab_label_;
        if (shape == QTabBar::RoundedWest) {
            label = QFontMetrics(font()).elidedText(label, Qt::ElideMiddle, 100);
        }

        int tab_id = -1;
        if (icons && text)
            tab_id = bar->addTab(item.tab_icon_, label);
        else if (icons)
            tab_id = bar->addTab(item.tab_icon_, QString());
        else if (text)
            tab_id = bar->addTab(label);

        // Adds tooltips only in Tabs mode or IconOnlyTabs mode
        // TODO in tab mode, show only if not elided, complicated since this doesn't inherit from QTabWidget
        if (shape == QTabBar::RoundedNorth && ((!text && icons) || (text && !icons)))
            bar->setTabToolTip(tab_id, item.tab_label_);
    }

    bar->setCurrentIndex(stack_->currentIndex());
    connect(bar, SIGNAL(currentChanged(int)), SLOT(ShowWidget(int)));
    connect(bar, SIGNAL(tabDoubleClicked(int)), this, SIGNAL(tabDoubleClicked(int)));
    tab_bar_ = bar;
}




//////
// FancyTabContainer
//////

FancyTabContainer::FancyTabContainer(QWidget* parent)
    : QWidget(parent),
      m_mode(Mode_None),
      m_currentIndex(-1),
      m_tabBar(NULL),
      m_stack(new QStackedLayout),
      m_sideWidget(new QWidget),
      m_sideLayout(new QVBoxLayout),
      m_topLayout(new QVBoxLayout),
      m_useBackground(false),
      m_singleWidgetMode(false),
      m_menu(NULL),
      m_proxyStyle(new FancyTabProxyStyle)
{
    m_sideLayout->setSpacing(0);
    m_sideLayout->setMargin(0);
    m_sideLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding));

    m_sideWidget->setLayout(m_sideLayout);
    m_sideWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);

    m_topLayout->setMargin(0);
    m_topLayout->setSpacing(0);
    m_topLayout->addLayout(m_stack);

    QHBoxLayout* main_layout = new QHBoxLayout;
    main_layout->setMargin(0);
    main_layout->setSpacing(1);
    main_layout->addWidget(m_sideWidget);
    main_layout->addLayout(m_topLayout);
    setLayout(main_layout);
}


void FancyTabContainer::setSingleWidget(QWidget * widget)
{
    m_singleWidgetMode = true;
    m_stack->addWidget(widget);
    m_stack->setCurrentIndex(0);
}


void FancyTabContainer::addTab(QWidget* tab, const QIcon& icon, const QString& label)
{
    m_stack->addWidget(tab);
    m_items << Item(icon, label);

//    if (!m_tabBar)
//        setMode(m_mode);
    if (FancyTabBar* bar = qobject_cast<FancyTabBar*>(m_tabBar)) {
        bar->addTab(icon, label);
    } else if (QTabBar* bar = qobject_cast<QTabBar*>(m_tabBar)) {
        bar->blockSignals(true);
        bar->addTab(icon, label);
        bar->blockSignals(false);
    }

    show();
}


void FancyTabContainer::addTab(const QIcon& icon, const QString& label)
{
    m_items << Item(icon, label);

//    if (!m_tabBar)
//        setMode(m_mode);
    if (FancyTabBar* bar = qobject_cast<FancyTabBar*>(m_tabBar)) {
        bar->addTab(icon, label);
    } else if (QTabBar* bar = qobject_cast<QTabBar*>(m_tabBar)) {
        bar->blockSignals(true);
        bar->addTab(icon, label);
        bar->blockSignals(false);
    }

    show();
}


void FancyTabContainer::addSpacer(int size, QSizePolicy::Policy vPolicy)
{
    m_items << Item(size, vPolicy);
    if (FancyTabBar* bar = qobject_cast<FancyTabBar*>(m_tabBar)) {
        bar->addSpacer(size, vPolicy);
    }
}


void FancyTabContainer::removeTab(const QString &label, bool deleteChildWidget)
{
    int index=-1;
    for (int i=0; i< m_items.count(); i++)
        if (m_items[i].tabLabel == label) {
            index = i;
            break;
        }
    if (index > -1) {
        if (!m_singleWidgetMode) {
            QWidget * widget = m_stack->widget(index);
            m_stack->removeWidget(widget);
            if (deleteChildWidget)
                delete widget;
        }
        m_items.removeAt(index);
        if (FancyTabBar* bar = qobject_cast<FancyTabBar*>(m_tabBar)) {
            bar->removeTab(index);
        } else if (QTabBar* bar = qobject_cast<QTabBar*>(m_tabBar)) {
            bar->blockSignals(true);
            bar->removeTab(index);
            bar->blockSignals(false);
        }
    }

    if (!m_items.size())
        hide();
}


void FancyTabContainer::removeAllTabs(bool deleteChildrenWidgets)
{
    if (!m_singleWidgetMode)
        for (int i=m_items.count()-1; i>=0; --i ) {
            QWidget * widget = m_stack->widget(i);
            m_stack->removeWidget(widget);
            if (deleteChildrenWidgets)
                delete widget;
        }

    if (FancyTabBar* bar = qobject_cast<FancyTabBar*>(m_tabBar)) {
        bar->removeAllTabs();
    } else if (QTabBar* bar = qobject_cast<QTabBar*>(m_tabBar)) {
        for(int i = bar->count()-1; i>=0; --i) {
            bar->blockSignals(true);
            bar->removeTab(i);
            bar->blockSignals(false);
        }
    }
    m_items.clear();
    m_currentIndex = -1;
    hide();
}


int FancyTabContainer::count()
{
    return m_items.count();
}


void FancyTabContainer::setBackgroundPixmap(const QPixmap& pixmap)
{
    m_backgroundPixmap = pixmap;
    update();
}


void FancyTabContainer::paintEvent(QPaintEvent*)
{
    if (!m_useBackground)
        return;

    QPainter painter(this);

    QRect rect = m_sideWidget->rect().adjusted(0, 0, 1, 0);
    rect = style()->visualRect(layoutDirection(), geometry(), rect);
    Utils::StyleHelper::verticalGradient(&painter, rect, rect);

    if (!m_backgroundPixmap.isNull()) {
        QRect pixmapRect(m_backgroundPixmap.rect());
        pixmapRect.moveTo(rect.topLeft());

        while (pixmapRect.top() < rect.bottom()) {
            QRect sourceRect(pixmapRect.intersected(rect));
            sourceRect.moveTo(0, 0);
            painter.drawPixmap(pixmapRect.topLeft(), m_backgroundPixmap, sourceRect);
            pixmapRect.moveTop(pixmapRect.bottom() - 10);
        }
    }

    painter.setPen(Utils::StyleHelper::borderColor());
    painter.drawLine(rect.topRight(), rect.bottomRight());

    QColor light = Utils::StyleHelper::sidebarHighlight();
    painter.setPen(light);
    painter.drawLine(rect.bottomLeft(), rect.bottomRight());
}


int FancyTabContainer::currentIndex() const
{
    return m_currentIndex;
}


QString FancyTabContainer::currentText() const
{
    return m_items.isEmpty() || m_currentIndex >= m_items.count() || m_currentIndex < 0
            ? QString() : m_items.at(m_currentIndex).tabLabel;
}


void FancyTabContainer::setCurrentIndex(int index)
{
    m_currentIndex = index;
    if (FancyTabBar* bar = qobject_cast<FancyTabBar*>(m_tabBar)) {
        bar->setCurrentIndex(index);
    } else if (QTabBar* bar = qobject_cast<QTabBar*>(m_tabBar)) {
        bar->setCurrentIndex(index);
    } else {
        if (!m_singleWidgetMode)
            m_stack->setCurrentIndex(index);
    }
}


void FancyTabContainer::changeText(const QString & text, int index)
{
    if (FancyTabBar* bar = qobject_cast<FancyTabBar*>(m_tabBar)) {
        bar->setTabText(text, index);
    } else if (QTabBar* bar = qobject_cast<QTabBar*>(m_tabBar)) {
        bar->setTabText(index, text);
    }
    m_items[index].tabLabel = text;
}


QString FancyTabContainer::tabText(int index)
{
    //    if (FancyTabBar* bar = qobject_cast<FancyTabBar*>(tabBar)) {
    //      return bar->tabText(index);
    //    } else if (QTabBar* bar = qobject_cast<QTabBar*>(tabBar)) {
    //      return bar->tabText(index);
    //    }
    if (index>=0 && index<m_items.count())
        return m_items[index].tabLabel;
    else
        return QString();
}


void FancyTabContainer::showWidget(int index)
{
    m_currentIndex = index;
    m_stack->setCurrentIndex(index);
    emit currentChanged(index);
}


void FancyTabContainer::addBottomWidget(QWidget* widget)
{
    m_topLayout->addWidget(widget);
}

void FancyTabContainer::updateState()
{
    setMode(m_mode);
    //    if (current_index_>=0) {
    //        this->blockSignals(true);
    //        SetCurrentIndex(current_index_);
    //        this->blockSignals(false);
    //    }
}


void FancyTabContainer::setMode(Mode mode)
{
    // Remove previous tab bar
    delete m_tabBar;
    m_tabBar = NULL;

    m_useBackground = false;

    // Create new tab bar
    switch (mode) {
    case Mode_None:
    default:
        qWarning() << "Unknown fancy tab mode" << mode;
        // fallthrough

    case Mode_LargeSidebar: {
        FancyTabBar* bar = new FancyTabBar(this);
        m_sideLayout->insertWidget(0, bar);
        m_tabBar = bar;

        foreach (const Item& item, m_items) {
            if (item.type == Item::Type_Spacer)
                bar->addSpacer(item.spacerSize);
            else
                bar->addTab(item.tabIcon, item.tabLabel);
        }

        bar->setCurrentIndex(m_stack->currentIndex());
        connect(bar, SIGNAL(currentChanged(int)), SLOT(showWidget(int)));
        connect(bar, SIGNAL(tabDoubleClicked(int)), this, SIGNAL(tabDoubleClicked(int)));

        m_useBackground = true;

        break;
    }

    case Mode_Tabs:
        makeTabBar(QTabBar::RoundedNorth, true, false, false);
        break;

    case Mode_IconOnlyTabs:
        makeTabBar(QTabBar::RoundedNorth, false, true, false);
        break;

    case Mode_SmallSidebar:
        makeTabBar(QTabBar::RoundedWest, true, true, true);
        m_useBackground = true;
        break;

    case Mode_PlainSidebar:
        makeTabBar(QTabBar::RoundedWest, true, true, false);
        break;
    }

    m_tabBar->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);

    m_mode = mode;
    emit modeChanged(mode);
    update();
}


void FancyTabContainer::contextMenuEvent(QContextMenuEvent* e)
{
    if ((m_sideWidget && !m_sideWidget->geometry().contains(e->pos())) &&
            (m_tabBar && !m_tabBar->geometry().contains(e->pos())))
        return;

    if (!m_menu) {
        m_menu = new QMenu(this);

        QSignalMapper* mapper = new QSignalMapper(this);
        QActionGroup* group = new QActionGroup(this);
        addMenuItem(mapper, group, tr("Large sidebar"), Mode_LargeSidebar);
        addMenuItem(mapper, group, tr("Small sidebar"), Mode_SmallSidebar);
        addMenuItem(mapper, group, tr("Plain sidebar"), Mode_PlainSidebar);
        addMenuItem(mapper, group, tr("Tabs on top"), Mode_Tabs);
        addMenuItem(mapper, group, tr("Icons on top"), Mode_IconOnlyTabs);
        m_menu->addActions(group->actions());

        connect(mapper, SIGNAL(mapped(int)), SLOT(setMode(int)));
    }

    m_menu->popup(e->globalPos());
}


void FancyTabContainer::addMenuItem(QSignalMapper* mapper, QActionGroup* group,
                                    const QString& text, Mode mode) {
    QAction* action = group->addAction(text);
    action->setCheckable(true);
    mapper->setMapping(action, mode);
    connect(action, SIGNAL(triggered()), mapper, SLOT(map()));

    if (mode == m_mode)
        action->setChecked(true);
}

void FancyTabContainer::makeTabBar(QTabBar::Shape shape, bool text, bool icons,
                                   bool fancy) {
    TabBar* bar = new TabBar(this);
    bar->setShape(shape);
    bar->setDocumentMode(true);
    bar->setUsesScrollButtons(true);
    bar->setElideMode(Qt::ElideRight);

    if (shape == QTabBar::RoundedWest) {
        bar->setIconSize(QSize(22, 22));
    }

    if (fancy) {
        bar->setStyle(m_proxyStyle.data());
    }

    if (shape == QTabBar::RoundedNorth)
        m_topLayout->insertWidget(0, bar);
    else
        m_sideLayout->insertWidget(0, bar);

    foreach (const Item& item, m_items) {
        if (item.type != Item::Type_Tab)
            continue;

        QString label = item.tabLabel;
        if (shape == QTabBar::RoundedWest) {
            label = QFontMetrics(font()).elidedText(label, Qt::ElideMiddle, 100);
        }

        int tab_id = -1;
        if (icons && text)
            tab_id = bar->addTab(item.tabIcon, label);
        else if (icons)
            tab_id = bar->addTab(item.tabIcon, QString());
        else if (text)
            tab_id = bar->addTab(label);

        // Adds tooltips only in Tabs mode or IconOnlyTabs mode
        // TODO in tab mode, show only if not elided, complicated since this doesn't inherit from QTabWidget
        if (shape == QTabBar::RoundedNorth && ((!text && icons) || (text && !icons)))
            bar->setTabToolTip(tab_id, item.tabLabel);
    }

    bar->setCurrentIndex(m_stack->currentIndex());
    connect(bar, SIGNAL(currentChanged(int)), SLOT(showWidget(int)));
    connect(bar, SIGNAL(tabDoubleClicked(int)), this, SIGNAL(tabDoubleClicked(int)));
    m_tabBar = bar;
}


